/*
 * Copyright (c) 2012 Johan Fylling
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 */

library TopDownExample;
import "dart:html";
import "dart:math" as Math;
import 'dart:json' as json;
import "package:logging/logging.dart" as Logging;
import "../example_lib.dart";
import "../../client/client_lib.dart";
import "../../engine/engine_lib.dart";
import "../../math/math_lib.dart";
import "../../tools/tools_lib.dart";
part "bullet.dart";
part "player.dart";
part "player_controller.dart";
part "living_actor.dart";
part "rifle.dart";
part "tool_percept.dart";


class DebugController extends LogicController {
  void update (Percept percept) {
    Set<int> seenActors = percept.seenActors;
    Vector2 enemyPosition = (seenActors.isEmpty ? null : percept.getPosition(seenActors.first));
    
    if (enemyPosition != null) {
      MovementAction action = new MovementAction();
      action.direction = enemyPosition - percept.position;
      action.orientation = action.direction;
      action.speed = 1.0;
      percept.movementAction = action;
    } else {
      MovementAction action = new MovementAction();
      action.speed = 0.0;
      percept.movementAction = action;
    }
  }
  
  void use (Percept percept, Percept userPercept) {
  }
  
  toJson () {
    var data = super.toJson();
    assert(data is Map);
    data["type"] = "debug_controller";
    return data;
  }
}


class Enemy extends LivingActor {
  Enemy (DeathCallback deathCallback) : super(new DebugController(), deathCallback);
}


class TopDownExample implements Example {
  static final Logging.Logger _logger               = new Logging.Logger("drones.example.top_down_example");
  WebGameEngine               _engine;
  CanvasEngine                _graphicsEngine;
  PhysicsEngine               _physicsEngine;
  ParticleWorldState          _worldState;
  BroadphaseDetector          _broadphaseDetector;
  NarrowphaseDetector         _narrowphaseDetector;
  ContactDetector             _contactDetector;
  CollisionManager            _collisionManager;
  ConfigurationHandler        _configurationHandler;
  
  TopDownExample () {
    InputManager inputManager = new InputManager(query("#canvas"));
    ServiceManager.setService("InputManager", inputManager);
    
    StaticIntegrator integrator = new StaticIntegrator(32);
    this._physicsEngine = new ParticleEngine(integrator, 32, 0.9, new Vector2(0,0));
    this._graphicsEngine = new CanvasEngine("canvas", this._broadphaseDetector);
    ServiceManager.setService("DebugPainter", this._graphicsEngine);
    this._worldState = new ParticleWorldState();
    this._broadphaseDetector = new SapDetector();
    this._graphicsEngine.bpd = this._broadphaseDetector;
    this._narrowphaseDetector = new SatDetector();
    this._contactDetector = new ClippingContactDetector();
    this._collisionManager = new CollisionManager(this._broadphaseDetector, 
                                                  this._narrowphaseDetector, 
                                                  this._contactDetector);
    this._collisionManager.drawDebug = false;
    this._engine = new WebGameEngine(this._physicsEngine, 
                                     this._graphicsEngine, 
                                     this._worldState,
                                     this._collisionManager);
    
    this._configurationHandler = new ConfigurationHandler(this._graphicsEngine);
    
    Bullet.pe = this._physicsEngine;
    Bullet.ws = this._worldState;
  }
  
  void init () {
    Camera camera = this._graphicsEngine.camera;
    camera.scale = 20;
    
    this._configurationHandler.registerConfigurationToggle(Keyboard.KEY_1, this._graphicsEngine.toggleDrawDebug);
    this._configurationHandler.registerConfigurationToggle(Keyboard.KEY_2, this._graphicsEngine.toggleDrawDebugCollisions);
    this._configurationHandler.registerConfigurationToggle(Keyboard.KEY_3, this._graphicsEngine.toggleDrawDebugParticles);
    this._configurationHandler.registerConfigurationToggle(Keyboard.KEY_SPACE, () {this._engine.pause = !this._engine.pause;});
    
    StorageManager storageManager = (ServiceManager.getService("StorageManager") as StorageManager);
    if (storageManager != null) {
      storageManager.setHandler("player", new PlayerStorageHandler(_graphicsEngine.camera, actorDeathCallback, _broadphaseDetector));
      storageManager.setHandler("enemy", (StorageManager manager, Map attributes) {
        var positionAttributes = attributes["position"];
        assert(positionAttributes is Map);
        Vector2 position = new Vector2(positionAttributes["x"], positionAttributes["y"]);
        
        Enemy enemy = new Enemy(actorDeathCallback);
        enemy.body.position = position;
  
        return enemy;
      });
      storageManager.setHandler("debug_controller", (StorageManager manager, Map attributes) {
        return new DebugController();
      });
      storageManager.setHandler("player_controller", (StorageManager manager, Map attributes) {
        return new PlayerController(_graphicsEngine.camera);
      });
      storageManager.setHandler("rifle", (StorageManager manager, Map attributes) {
        return new Rifle();
      });
      
      String file = window.location.pathname;
      String path = file.substring(0, file.lastIndexOf('/'));
      storageManager.loadFile('$path/maps/test_map_1.json');
      
      print(storageManager.storeJson(_worldState));
    }
  }
  
  void actorDeathCallback (Actor actor) {
    print("Actor ${actor.id} is dead.");
    actor.attributes.remove("health");
    this._worldState.removeActor(actor);
  }
  
  void start () { this._engine.start(); }
  
  void stop () {}
}


void main() {
  Logging.hierarchicalLoggingEnabled = true;
  Logging.Logger logger = new Logging.Logger('drones');
  logger.level = Logging.Level.FINER;
  logger.on.record.add((Logging.LogRecord record) {
    print("${record.time} ${record.level} ${record.loggerName} - ${record.message}");
  });
  
  TopDownExample example = new TopDownExample();
  example.init();
  example.start();
}
